package com.wcj.common.dao.impl;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.PropertyUtils;
import org.hibernate.Criteria;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;

import com.wcj.common.Page;
import com.wcj.common.utils.BeanUtils;

public class WcjHibernateDao extends HibernateDaoSupport {
	/**
	 * 去除hql的orderby 子句，用于pagedQuery.
	 *
	 * @param hql
	 * 			hql语句
	 * @return
	 * 			去除orderby后的hql字符串
	 */
	private static String removeOrders(String hql) {
		Assert.hasText(hql);
		Pattern p = Pattern.compile("ORDER\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}

	/**
	 * 去除hql的from前的语句.
	 *
	 * @param hql
	 * 			hql语句
	 * @return
	 * 			去除后的hql字符串
	 */
	private static String removeSelect(String hql) {
		Assert.hasText(hql);
		int beginPos = hql.toLowerCase().indexOf("from");
		Assert.isTrue(beginPos >= 0, " hql : " + hql + " must has a keyword 'from'");
		return hql.substring(beginPos);
	}
	
	/**
	 * 根据属性名和属性值查询对象.
	 *
	 * @param entityClass
	 *            泛型T的类型
	 * @param name
	 *            查询条件属性名
	 * @param value
	 *            查询条件
	 * @return 符合条件的唯一对象
	 */
	public Object genericFindUniqueBy(Class entityClass, String name, Object value) {
		Assert.hasText(name);
		Criteria criteria = DetachedCriteria.forClass( entityClass ).getExecutableCriteria( this.getSession() );
		criteria.add(Restrictions.eq(name, value));	
		return criteria.uniqueResult();
	}
	
	/**
	 * 根据属性名和属性值以Like AnyWhere方式查询对象.
	 *
	 * @param entityClass
	 *            泛型T的类型
	 * @param name
	 *            查询条件属性名
	 * @param value
	 *            查询条件
	 * @return 符合条件的对象列表
	 */
	public List genericFindByLike(Class entityClass, String name, String value) {
		Assert.hasText(name);
		Criteria criteria = DetachedCriteria.forClass( entityClass ).getExecutableCriteria( this.getSession() );
		criteria.add(Restrictions.like(name, value, MatchMode.ANYWHERE));
		return criteria.list();
	}
	
	/**
	 * 判断对象某些属性的值在数据库中不存在重复.
	 *
	 * @param entityClass
	 *            类型
	 * @param entity
	 *            指定的实体
	 *
	 * @param names
	 *            在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
	 * @return 包含重复的内容返回true，否则返回false
	 */
	public boolean isGenericNotUnique(Class entityClass, Object entity, String names) {
		Assert.hasText(names);
		Criteria criteria = DetachedCriteria.forClass( entityClass ).getExecutableCriteria( this.getSession() ).setProjection(Projections.rowCount());
		String[] nameList = names.split(",");
		try {
			// 循环加入
			for (String name : nameList) {
				criteria.add(Restrictions.eq(name, PropertyUtils.getProperty(entity, name)));
			}
			// 以下代码为了如果是update的情况,排除entity自身.
			// 通过Hibernate的MetaData接口取得主键名
			String idPropertyName = getSessionFactory().getClassMetadata(entity.getClass()).getIdentifierPropertyName();
			if (idPropertyName != null) {
				// 通过反射取得entity的主键值
				Object id = PropertyUtils.getProperty(entity, idPropertyName);
				// 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断
				if (id != null)
					criteria.add(Restrictions.not(Restrictions.eq(idPropertyName, id)));
			}
		} catch (IllegalAccessException e) {
			logger.error("Error when reflection on entity", e);
			return false;
		} catch (InvocationTargetException e) {
			logger.error("Error when reflection on entity", e);
			return false;
		} catch (NoSuchMethodException e) {
			logger.error("Error when reflection on entity", e);
			return false;
		}
		return (Integer) criteria.uniqueResult() > 0;
	}
	
	/**
	 * 使用指定查询对象进行分页查询.
	 *
	 * @param criteria
	 *            实体的查询对象
	 * @param pageNo
	 *            页号,从1开始.
	 * @param pageSize
	 *            每页中的记录数
	 * @return 当前页的分页对象
	 */
	public Page pagedQuery(Criteria criteria, int pageNo, int pageSize) {
		CriteriaImpl impl = (CriteriaImpl) criteria;
		// 先把Projection和OrderBy条件取出来,清空两者来执行Count操作
		Projection projection = impl.getProjection();
		List<CriteriaImpl.OrderEntry> orderEntries;
		try {
			orderEntries = (List) BeanUtils.getPrivateProperty(impl, "orderEntries");
			BeanUtils.setPrivateProperty(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			logger.error(e.getMessage());
			throw new InternalError(" Runtime Exception impossibility throw ");
		}
		// 执行查询
		long totalCount = ((Number) criteria.setProjection(Projections.rowCount()).uniqueResult()).longValue();
		// 将之前的Projection和OrderBy条件重新设回去
		criteria.setProjection(projection);
		if (projection == null) {
			criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		try {
			BeanUtils.setPrivateProperty(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			logger.error(e.getMessage());
			throw new InternalError(" Runtime Exception impossibility throw ");
		}
		// 返回分页对象
		if (totalCount < 1)
			return new Page();
		int startIndex = Page.getStartOfPage(pageNo, pageSize);
		List list = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
		return new Page(startIndex, totalCount, pageSize, list);
	}
	
	public Page pagedQuery( DetachedCriteria criteria, int pageNo, int pageSize) {
		return pagedQuery( criteria.getExecutableCriteria( this.getSession() ) , pageNo, pageSize );
	}
}